#include <stdio.h>
#include <errno.h>

#include "commproto.h"
#include "tknetbusi.h"
#include "constants.h"
#include "tkerror.h"
#include "tklog.h"
#include "tkevent.h"
#include "proto.h"
#include "tknetbusi.h"
#include "tkhandler.h"

/*
EVENT_TYPE: 
EVENT_LOCAL_ERROR(rw)
EVENT_REMOTE_CLOSE(rw)
EVENT_READ
EVENT_WRITE
*/

/* use for recv file */
static unsigned char tmp_recv_file_buf[4096];	
static int tmp_had_recv_buf_len;

/* in main.c */
extern int do_broadcast_message(struct tk_connect *conn, struct tk_message *msg, message *net_msg);
extern int add_client_conn(struct tk_connect *conn);

static int hd_get_client_information(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type);
static int hd_send_clients_list_information(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type);
static int hd_communicating(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type);

/* the fact send respstatus function
@PARAM
	pv: next handler when success 
@RETURN
	TK_OK: send the resp status message
	TK_AGAIN: send partial of resp status message(should call hd_do_send_respstatus again)
	TK_ERROR: send message error 
*/
int hd_do_send_respstatus(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type)
{
        struct tk_connect *conn = (struct tk_connect*)s->conn_ptr;
        struct tk_message *tmsg = &conn->recv_msg;
	struct tk_handler_param *handlerParam = (struct tk_handler_param*)pv;
	event_handler nextHander = handlerParam->handler;
	int handlerType = handlerParam->handlerType;
	int r = 0;

        if (s->finished) {
                elog("areadly finish socket can't send status");
                return TK_ERROR;
        }

        if (event_type & EVENT_LOCAL_ERROR) {
                elog("send resp status occur error");
                s->finished = 1;
                s->merrno = TK_EVENT_TYPE_ERROR;
                  
                return TK_ERROR;
        }             
        else if (event_type & EVENT_REMOTE_CLOSE) {
                elog("send resp status occur client close");
                s->finished = 1;
                return TK_OK;
        }

	/* notice: send at most one msg(resp status msg, other msg in connect will send delay */
        do {
                r = tk_do_send(s);
               	if (r == TK_NO_MORE_SEND_MSG || r == TK_SEND_BODY_DONE) {
			/* next handler */
			s->handler = nextHander;
			if (handlerType & TK_HANDLER_TYPE_R) {
				epoll_set_event(env, s->event, EPOLL_EVENT_R);
			}
			else if (handlerType & TK_HANDLER_TYPE_W) {
				epoll_set_event(env, s->event, EPOLL_EVENT_W);
			}
			else {  /* rw */
				epoll_set_event(env, s->event, EPOLL_EVENT_RW);
			}

			set_custom_socket_pv(s, NULL);

			return TK_OK;
                }
                else if (r == TK_ERROR) {
                        s->failed = 1;
                        s->merrno = TK_DO_SEND_ERROR;
                        s->eerrno = errno;
                        errno = 0;
                        elog("hd_communicating send failed");

                        return TK_ERROR;
                }

        } while (r == TK_AGAIN);

	return TK_AGAIN;	
}

int tk_send_respstatus(struct epoll_env *env, struct custom_socket *s)
{
        struct tk_connect *conn = (struct tk_connect*)s->conn_ptr;
        struct tk_message tmsg;
	void *pv = get_custom_socket_pv(s);
        struct tk_handler_param *handlerParam = (struct tk_handler_param*)pv;
        event_handler nextHander = handlerParam->handler;
        int handlerType = handlerParam->handlerType;

	const char *errorMsg = NULL;
	respstatus resp;
	int errorFlag = 0;
	int r = 0;
	
	if (s->finished) {
		elog("areadly finish socket can't send status");
		return TK_ERROR;
	}

	if (s->failed) {
		/* modify status finished */
		s->finished = 1;
		errorFlag = 1;
		errorMsg = error_desc(s->merrno);
	}

	memset(&resp, 0x00, sizeof(respstatus));
	if (errorFlag) {
		/* send failed message */
		resp.status = s->merrno;
		if (errorMsg != NULL) {
			strcpy(resp.errormsg, errorMsg);
		}
	}
	else {
		/* send ok message */
		resp.status = TK_OK;
		strcpy(resp.errormsg, "ok");
	}

	memset(&tmsg, 0x00, sizeof(tmsg));
	SERIAL_TO_TK_MESSAGE(&resp, &tmsg, respstatus, MSG_TYPE_STATUS, r); 
	if (r) {
		elog("serial to tk_message failed");
		/* origin send ok response */
		if (!s->failed)
			s->failed = 1;

		s->merrno = TK_JSON_SERIALIZE;

		return TK_ERROR;		
	}

	r = tk_do_add_send_msg(s, &tmsg);
	if (r == TK_NEED_ADD_W_EVENT) {
		r = epoll_add_write_event(env, s->event);
		if (r) {
			s->finished = 1;
			elog("client[%s]-ip[%s] failed, force closed", conn->client.clientname, conn->csock->addr.ipv4);
			return TK_ERROR;
		}
	}

	/* send resp status should immediatly, give one mannual chance to improve priority */
	if ((r = hd_do_send_respstatus(env, s, pv, EVENT_WRITE)) == TK_OK) {
		/* send success ont time, set next handler */
		/* next handler */
                s->handler = nextHander;
                if (handlerType & TK_HANDLER_TYPE_R) {
			epoll_set_event(env, s->event, EPOLL_EVENT_R);
		}
		else if (handlerType & TK_HANDLER_TYPE_W) {
			epoll_set_event(env, s->event, EPOLL_EVENT_W);
		}
		else {  /* rw */
			epoll_set_event(env, s->event, EPOLL_EVENT_RW);
		}

		set_custom_socket_pv(s, NULL);
	}
	else if (r == TK_AGAIN) {
		s->handler = hd_do_send_respstatus;
	}
	else {	/* TK_ERROR */ 
		elog("send response status error");
		s->finished = 1;

		return TK_ERROR;
	}

	return TK_OK;

}

/* @NOUSE */
int tk_recv_respstatus(struct epoll_env *env, struct custom_socket *s)
{
        struct tk_connect *conn = (struct tk_connect*)s->conn_ptr;
        struct tk_message tmsg;
	void *pv = get_custom_socket_pv(s);
	struct tk_handler_param *handlerParam = (struct tk_handler_param*)pv;
	event_handler nextHander = handlerParam->handler;
	int handlerType = handlerParam->handlerType;

	const char *errorMsg = NULL;
	respstatus resp;
	int errorFlag = 0;
	int r = 0;
	
	if (s->finished) {
		elog("areadly finish socket can't send status");
		return TK_ERROR;
	}

	if (s->failed) {
		/* modify status finished */
		s->finished = 1;
		errorFlag = 1;
		errorMsg = error_desc(s->merrno);
	}

	memset(&resp, 0x00, sizeof(respstatus));
	if (errorFlag) {
		/* send failed message */
		resp.status = s->merrno;
		if (errorMsg != NULL) {
			strcpy(resp.errormsg, errorMsg);
		}
	}
	else {
		/* send ok message */
		resp.status = TK_OK;
		strcpy(resp.errormsg, "ok");
	}

	memset(&tmsg, 0x00, sizeof(tmsg));
	SERIAL_TO_TK_MESSAGE(&resp, &tmsg, respstatus, MSG_TYPE_STATUS, r); 
	if (r) {
		elog("serial to tk_message failed");
		/* origin send ok response */
		if (!s->failed)
			s->failed = 1;

		s->merrno = TK_JSON_SERIALIZE;

		return TK_ERROR;		
	}

	r = tk_do_add_send_msg(s, &tmsg);
	if (r == TK_NEED_ADD_W_EVENT) {
		r = epoll_add_write_event(env, s->event);
		if (r) {
			s->finished = 1;
			elog("client[%s]-ip[%s] failed, force closed", conn->client.clientname, conn->csock->addr.ipv4);
			return TK_ERROR;
		}
	}

	/* send resp status should immediatly, give one mannual chance to improve priority */
	if ((r = hd_do_send_respstatus(env, s, pv, EVENT_WRITE)) == TK_OK) {
		/* send success ont time, do nothing */
	}
	else if (r == TK_AGAIN) {
		s->handler = hd_do_send_respstatus;
	}
	else {	/* TK_ERROR */ 
		elog("send response status error");
		s->finished = 1;

		return TK_ERROR;
	}

	return TK_OK;

}

int after_accept_entry_impl(struct epoll_env *env, struct custom_socket *s, void *pv)
{
	int r = 0;
	struct tk_connect *conn = NULL;

	conn = get_tk_connect(s);
	conn->stage = TK_CONNECT_SYNC_CLIENT_INFO;

	/* add read event */
	r = epoll_set_event(env, s->event, EPOLL_EVENT_R);
	if (r) {
		elog("add read event failed");
		return -1;
	}
				
	/* set read handler */	
	s->handler = hd_get_client_information;
	
	return 0;
}

int after_connect_entry_impl(struct custom_socket *s, void *pv)
{
	return 0;
}

/* first application handler */
static int hd_get_client_information(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type)
{
	dlog("hd_get_client_information start");
	struct tk_connect *conn = (struct tk_connect*)s->conn_ptr;
	struct tk_message *tmsg = &conn->recv_msg;
	clientinfo client;
	memberlist *memlist;
	member mem;
	int len;
	int r, rc;
	
        if (event_type != EVENT_READ) {
                elog("hd_get_client_information raised by not read event");
		s->failed = 1;
		s->merrno = TK_EVENT_TYPE_ERROR;

                return TK_ERROR;
        }

	/*
	1. TK_OK (head received)
	2. TK_AGAIN
	3. TK_ERROR
	4. TK_DONE      (remote close)
	5. TK_RECV_BODY_DONE
	*/
	r = tk_do_recv(s);
	if (r == TK_RECV_BODY_DONE) {
		epoll_set_event(env, s->event, EPOLL_EVENT_W);
		len = tmsg->body_len;
		if (tmsg->message_type != MSG_TYPE_CLIENT_INFO) {
			elog("message is not MSG_TYPE_CLIENT_INFO");
			return TK_MESSAGE_TYPE_ERROR; 	
		}
		memset(&client, 0x00, sizeof(client));
		DESERIAL_FROM_TK_MESSAGE(&client, tmsg, clientinfo, r);
		if (r) {
			elog("DESERIAL_FROM_TK_MESSAGE clientinfo failed");
			return TK_JSON_DESERIALIZE;
		}

		ilog("%d - %s", client.clientid, client.clientname);
		/* send resp */
		
		memset(&conn->handlerParam, 0x00, sizeof(struct tk_handler_param));
		conn->handlerParam.handler = hd_send_clients_list_information;
		conn->handlerParam.handlerType = TK_HANDLER_TYPE_W;

		set_custom_socket_pv(s, &conn->handlerParam);

		r = tk_send_respstatus(env, s);
		if (r) {
			s->finished = 1;
			return TK_ERROR;
		}

		/* insert app environment's memeber list */
		memset(&mem, 0x00, sizeof(mem));
		mem.clientid = client.clientid;
		strcpy(mem.clientname, client.clientname); 
		strcpy(mem.ip, s->addr.ipv4);

		/* update connect's client info */
		conn->client.clientid = client.clientid; 
		strcpy(conn->client.clientname, client.clientname); 
		strcpy(conn->client.ip, s->addr.ipv4);

		/* insert into client list in server env */
		if (add_client_conn(conn)) {
			s->failed = 1; 
			elog("add_client_conn failed");	
			return TK_ERROR;
		}
		
		r = main_insert_member(&mem);
		if (r) {
			elog("main_insert_member failed");
			s->failed = 1;	
			s->merrno = r;

			return TK_ERROR;
		}

		/* do send information */
		/* add write handler and del read handler */
		epoll_set_event(env, s->event, EPOLL_EVENT_W);
		s->handler = hd_send_clients_list_information;
		conn->stage = TK_CONNECT_SYNC_CLIENTS_LIST;
	}
	else if (r == TK_ERROR) {
		elog("hd_get_client_information failed");
		s->failed = 1; 
		return TK_ERROR;
	}	
	else if (r == TK_DONE) {
		elog("hd_get_client_information remote close");
		s->finished = 1;
	}
	else if (r == TK_AGAIN) {
		return r;
	}

	dlog("hd_get_client_information end");

        return TK_OK;
}

static int hd_send_clients_list_information(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type)
{
	dlog("hd_send_clients_list_information start");
	int r;
	struct tk_connect *conn = (struct tk_connect*)s->conn_ptr;

        if (event_type & EVENT_LOCAL_ERROR) {
                s->failed = 1;
		s->merrno = TK_EVENT_TYPE_ERROR;

                return TK_OK;
        }
        else if (event_type & EVENT_REMOTE_CLOSE) {
                s->finished = 1;
                return TK_OK;
        }

	if (event_type != EVENT_WRITE) {
                elog("hd_send_clients_list_information raised by write event");
                return TK_ERROR;
	}

	/* send initialize clients information */
	do {
		/*
		1. TK_AGAIN
		2. TK_ERROR(errno)
		3. TK_DONE(nouse)
		4. TK_OK (head or body sent)
		5. TK_NO_MORE_SEND_MSG (attention: other success status)
		*/
		/* notice: member list information will be set in do_send_init_member_list(after this event loop) */
		r = tk_do_send(s);
		if (r == TK_NO_MORE_SEND_MSG) {
			epoll_set_event(env, (void*)s->event, EPOLL_EVENT_R);
			/* change handler */
			s->handler = hd_communicating;
			conn->stage = TK_CONNECT_COMMUNICATING;
		}
		else if (r == TK_ERROR) {
			s->failed = 1;
			s->merrno = r;

			elog("hd_send_clients_list_information failed");

			return TK_ERROR;
		}
		else if (r == TK_AGAIN) {
			return r;
		}

	} while (r == TK_SEND_BODY_DONE);

	dlog("hd_send_clients_list_information end");

	return TK_OK;
}

static int hd_file_recving(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type)
{
	dlog("hd_file_recving start");

	

	dlog("hd_file_recving end");
}

/* catch read and write event */
/* 
typedef struct
{
        int     groupid ;
        char    groupname[ 50 + 1 ] ;
        int     clientid ;
        char    clientname[ 50 + 1 ] ;
        int     contentlen ;
        char    content[ 400 + 1 ] ;
} message ;
*/
static int hd_communicating(struct epoll_env *env, struct custom_socket *s, void *pv, int event_type)
{
	dlog("hd_communicating start");

	int r = 0;
	struct tk_connect *conn = (struct tk_connect*)s->conn_ptr;
	char tmp_cache_str_time[30];
	int i = 0;
	FILE* recv_file = NULL;

	if (event_type & EVENT_LOCAL_ERROR) {
		elog("clientname[%s] : ip[%s] client failed", conn->client.clientname, s->addr.ipv4);
		s->failed = 1;

		s->merrno = TK_EVENT_TYPE_ERROR;
		return TK_ERROR;
	}
	else if (event_type & EVENT_REMOTE_CLOSE) {
		elog("clientname[%s] : ip[%s] client leave", conn->client.clientname, s->addr.ipv4);
		s->finished = 1;
		return TK_OK;
	}

	if (event_type & EVENT_READ) {
		do {
			r = tk_do_recv(s);
			if (r == TK_RECV_BODY_DONE) {
				struct tk_message tk_msg;

				switch (conn->recv_msg.message_type) {

					ilog("-----------MESSAGE(0:member_list 1:client_info 2:comm_msg 3:logout 4:status 5:file)type[%d]-------",
														conn->recv_msg.message_type); 
#if 0
					case MSG_TYPE_FILE:
					{
						filemessage fmsg;
						memset(&fmsg, 0x00, sizeof(fmsg));
                                                DESERIAL_FROM_TK_MESSAGE(&msg, &conn->recv_msg, filemessage, r);
                                                if (r) {
                                                        elog("DESERIAL_FROM_TK_MESSAGE failed");
                                                        return TK_JSON_DESERIALIZE;
                                                }


						memset(tmp_cache_str_time, 0x00, sizeof(tmp_cache_str_time));
						tmp_cache_str_time[0] = '@';
						memset(tmp_cache_str_time + 1, cache_str_time, sizeof(cache_str_time));
						for (i = 0; i < strlen(tmp_cache_str_time); i++) {
							if (tmp_cache_str_time[i] == ' ') {
								tmp_cache_str_time[i] = '-';
							}
						}
						strcat(filemessage.filename, tmp_cache_str_time);	

						ilog("\n{\ngroup[%d] - groupname[%s]\nclientid[%d] - clientname[%s]\nfilename[%s]-filesize[%d]\n}", fmsg.filename, fmsg.filesize);
						if ((recv_file = create_recv_file(filemessage.filename)) == NULL) {
							elog("create filename[%s]", filemessage.filename);
						}
/*
@RETURN:        1. TK_OK
                2. TK_AGAIN
                3. TK_ERROR
                4. TK_DONE
                5. TK_BUFNOTENOUGH(special case)
                (recved len < need recved len & tcp internal had some data so the receive buffer too small)
*/
						r = tk_recvn(int fd, char *buf, int *buf_len)

						break;
					}
#endif
					case MSG_TYPE_COMM_MSG:
					{
						message msg;
						
						memset(&msg, 0x00, sizeof(msg));
						DESERIAL_FROM_TK_MESSAGE(&msg, &conn->recv_msg, message, r);
						if (r) {
							elog("DESERIAL_FROM_TK_MESSAGE failed");
							return TK_JSON_DESERIALIZE;
						}

						ilog("\n{\ngroup[%d] - groupname[%s]\nclientid[%d] - clientname[%s]\ncontent[%s]\n}", 
							msg.groupid, msg.groupname, msg.clientid, msg.clientname, msg.content);

						/* send to other clients */
						memcpy(&tk_msg, &conn->recv_msg, sizeof(struct tk_message));
						tk_msg.body_left_len = tk_msg.body_len;

						do_broadcast_message(conn, &tk_msg, &msg);
						break;
					}
					default:
					{
						ilog("-----------Unknow MESSAGE[%d]-------", conn->recv_msg.message_type); 
						break;
					}
				}

				continue;
			}
			else if (r == TK_ERROR) {
				s->failed = 1;
				s->merrno = TK_DO_READ_ERROR;
				s->eerrno = errno;
				errno = 0;
				elog("hd_communicating read failed");

				return TK_ERROR;
			}
			else if (r == TK_DONE) {
				elog("hd_communicating remote close");
				elog("clientname[%s] : ip[%s] client leave", conn->client.clientname, s->addr.ipv4);
				s->finished = 1;
				break;
			}
		} while (r == TK_AGAIN);
	}

	if (event_type & EVENT_WRITE) {
		do {
			r = tk_do_send(s);
			if (r == TK_NO_MORE_SEND_MSG) {
				/* del write event and add read event */
				/* write event will be auto added when has data in send buffer */
				epoll_set_event(env, s->event, EPOLL_EVENT_R);
				break;
			}
			else if (r == TK_ERROR) {
				s->failed = 1;
				s->merrno = TK_DO_SEND_ERROR;
				s->eerrno = errno;
				errno = 0;	
				elog("hd_communicating send failed");

				return TK_ERROR;
			}

		} while (r == TK_AGAIN || r == TK_SEND_BODY_DONE);
	}
	
	dlog("hd_communicating end");

	return TK_OK;
}
